/*
 * Copyright 2017-2021 Dromara.org

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dromara.hmily.demo.springcloud.inventory.service.impl;

import org.dromara.hmily.annotation.HmilyTCC;
import org.dromara.hmily.common.exception.HmilyRuntimeException;
import org.dromara.hmily.common.utils.CollectionUtils;
import org.dromara.hmily.core.context.HmilyContextHolder;
import org.dromara.hmily.core.context.HmilyTransactionContext;
import org.dromara.hmily.demo.common.inventory.dto.InventoryDTO;
import org.dromara.hmily.demo.common.inventory.entity.InventoryDO;
import org.dromara.hmily.demo.common.inventory.entity.LocalLogDO;
import org.dromara.hmily.demo.common.inventory.mapper.InventoryMapper;
import org.dromara.hmily.demo.common.inventory.mapper.LocalLogMapper;
import org.dromara.hmily.demo.springcloud.inventory.service.InventoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Objects;
import java.util.Optional;

/**
 * InventoryServiceImpl.
 *
 * @author xiaoyu
 */
@Service("inventoryService")
public class InventoryServiceImpl implements InventoryService {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(InventoryServiceImpl.class);

    private final InventoryMapper inventoryMapper;

    private final LocalLogMapper localLogMapper;

    @Autowired(required = false)
    public InventoryServiceImpl(InventoryMapper inventoryMapper,LocalLogMapper localLogMapper) {
        this.inventoryMapper = inventoryMapper;
        this.localLogMapper=localLogMapper;
    }

    /**
     * 扣减库存操作.
     * 这一个tcc接口
     *
     * @param inventoryDTO 库存DTO对象
     * @return true
     */
    @Override
    @HmilyTCC(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod")
    @Transactional
    public Boolean decrease(InventoryDTO inventoryDTO) {
        Long transId = HmilyContextHolder.get().getTransId();
        //查询事务记录表是否存在 幂等判断 悬挂处理
        if (CollectionUtils.isNotEmpty(localLogMapper.findByLocalLog(transId))){
            LOGGER.info("try,cancel,confirm已经执行无需继续执行（幂等或悬挂）,事务id:{}",transId);
            return Boolean.TRUE;
        }
        LOGGER.info("==========try扣减库存decrease===========");
        inventoryMapper.decrease(inventoryDTO);
        LocalLogDO localLogDO=new LocalLogDO();
        localLogDO.setTxNo(transId);
        localLogDO.setCreateTime(new Date());
        localLogDO.setType(1);
        localLogDO.setBusinessId(Long.parseLong(inventoryDTO.getProductId()));
        localLogMapper.save(localLogDO);
        return true;
    }
    
    @Override
    public Boolean testDecrease(InventoryDTO inventoryDTO) {
        inventoryMapper.testDecrease(inventoryDTO);
        return true;
    }
    
    /**
     * 获取商品库存信息.
     *
     * @param productId 商品id
     * @return InventoryDO
     */
    @Override
    public InventoryDO findByProductId(String productId) {
        return inventoryMapper.findByProductId(productId);
    }

    @Override
    @HmilyTCC(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod")
    @Transactional
    public Boolean mockWithTryException(InventoryDTO inventoryDTO) {
        //这里是模拟异常所以就直接抛出异常了
        Long hmilyTransactionContext = HmilyContextHolder.get().getTransId();
        throw new HmilyRuntimeException("库存扣减异常！shiwu quanjuidwei :"+hmilyTransactionContext);
    }

    @Override
    @HmilyTCC(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod")
    @Transactional(rollbackFor = Exception.class)
    public Boolean mockWithTryTimeout(InventoryDTO inventoryDTO) {
        try {
            //模拟延迟 当前线程暂停10秒
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("==========springcloud调用扣减库存mockWithTryTimeout===========");
        final int decrease = inventoryMapper.decrease(inventoryDTO);
        if (decrease != 1) {
            throw new HmilyRuntimeException("库存不足");
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmMethodTimeout(InventoryDTO inventoryDTO) {
        try {
            //模拟延迟 当前线程暂停11秒
            Thread.sleep(11000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("==========Springcloud调用扣减库存确认方法===========");
        inventoryMapper.decrease(inventoryDTO);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmMethodException(InventoryDTO inventoryDTO) {
        LOGGER.info("==========Springcloud调用扣减库存确认方法===========");
        final int decrease = inventoryMapper.decrease(inventoryDTO);
        if (decrease != 1) {
            throw new HmilyRuntimeException("库存不足");
        }
        return true;
        // throw new TccRuntimeException("库存扣减确认异常！");
    }
    
    public Boolean confirmMethod(InventoryDTO inventoryDTO) {
        LOGGER.info("==========confirmMethod库存确认方法===========");
        //幂等处理校验
        Long transId = HmilyContextHolder.get().getTransId();
        LOGGER.info("shiwuquanjuidwei"+transId);
        LocalLogDO byTxNoAndType = localLogMapper.findByTxNoAndType(transId, 3);
        if (Objects.nonNull(byTxNoAndType)){
            return Boolean.TRUE;
        }
        int confirm = inventoryMapper.confirm(inventoryDTO);
        LocalLogDO localLogDO=new LocalLogDO();
        localLogDO.setTxNo(transId);
        localLogDO.setCreateTime(new Date());
        localLogDO.setType(3);
        return  localLogMapper.save(localLogDO)>0;
    }

    public Boolean cancelMethod(InventoryDTO inventoryDTO) {
        LOGGER.info("==========cancelMethod库存取消方法===========");
        Long transId = HmilyContextHolder.get().getTransId();
        LOGGER.info("shiwuquanjuidwei"+transId);
        LocalLogDO byTxNoAndType = localLogMapper.findByTxNoAndType(transId, 1);
        //空回滚 try未执行，cancel执行
        if (Objects.isNull(byTxNoAndType)){
            return Boolean.TRUE;
        }
        //幂等校验
        LocalLogDO cancelTxNoAndType = localLogMapper.findByTxNoAndType(transId, 2);
        if (Objects.nonNull(cancelTxNoAndType)){
            return Boolean.TRUE;
        }
        //try存的参数 主键id（cancel直接使用）
        Long businessId = byTxNoAndType.getBusinessId();
        inventoryDTO.setProductId(String.valueOf(businessId));
        inventoryMapper.cancel(inventoryDTO);
        LocalLogDO localLogDO=new LocalLogDO();
        localLogDO.setTxNo(transId);
        localLogDO.setCreateTime(new Date());
        localLogDO.setType(2);
        return  localLogMapper.save(localLogDO)>0;
    }
}
